/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#ifndef H_OS_LINK_TABLES_
#define H_OS_LINK_TABLES_

/**
 * @brief Helper macros for accessing link time tables
 *
 * Link time tables are compile time constant tables that are
 * constructed during linking aggregating symbols for specific
 * linker section.
 * Object put in the section should be of the same size to be
 * treated as array elements.
 *
 * To create named section that can be used as link time table
 * without manual modification of linker script newt tool
 * recognizes following section in pkg.yml
 *
 * pkg.link_tables:
 *     - example_table1
 *     - example_table2
 *
 * This creates entries in autogenerated linker script include file
 * link_table.ld.h, those entries gather symbol of sections names:
 *  .example_table1
 *  .example_table1.*
 * and
 *  .example_table2
 *  .example_table2.*
 *
 * To put element in those section user code can specify section
 * name in variable definition.
 *
 * Following macro creates external table that can be access just
 * as it would be defined explicitly.
 *
 * struct foo {
 *     int bar;
 * };

 * LINK_TABLE(struct foo, example1_table)
 * LINK_TABLE(uint32_t, example2_table)
 *
 * Declares tables that are equivalent to:
 *
 * const struct foo example1_table[size1];
 * const uint32_t example2_table[size2];
 *
 * Size of table is unknown at compile time but can be determined
 * in runtime, depending on how many elements are actually put
 * in those arrays.
 *
 * To put element in such array user code has to specify matching
 * section name. It can be done in many ways i.e.:
 *
 * const struct foo foo1 __attribute__((section(".example1_table"), used)) = {1};
 * const struct foo foo2 LINK_TABLE_SECTION(example1_table) = {2};
 * const struct foo foo3 LINK_TABLE_ELEMENT_SECTION(example1_table, foo3) = {3};
 * LINK_TABLE_ELEMENT(example1_table, foo4) = {4};
 *
 * const uint32_t elem1 LINK_TABLE_SECTION(example2_table) = 1;
 * const uint32_t elem2 LINK_TABLE_ELEMENT_SECTION(example2_table, elem2) = 2;
 * LINK_TABLE_ELEMENT(example_table2, elem3) = 3;
 */

/* Macro change table name to actual symbol generated in link_tables.ld.h */
#define LINK_TABLE_START(table_name) __##table_name##_start__
/* Macro change table name to actual symbol generated in link_tables.ld.h */
#define LINK_TABLE_END(table_name)  __##table_name##_end__
#define LINK_TABLE_SIZE(table_name) table_name##_size()
/* Macro to create typedef for element in table.
 * Typedef name is constructed from table name and this allows to use table
 * later on without specifying element type
 */
#define LINK_TABLE_ELEMENT_TYPE(table_name) table_name##_element_t
/* Macro to create symbol attribute for table */
#define LINK_TABLE_SECTION(table_name)                                        \
    __attribute__((section("." #table_name), used))
/* Macro to create attribute for table with name that may affect order of elements */
#define LINK_TABLE_ELEMENT_SECTION(table_name, elem)                          \
    __attribute__((section("." #table_name "." #elem), used))
/* Macro to create element that goes to link table.
 * It could be used like this:
 * LINK_TABLE_ELEMENT(example1_table, foo5) = {
 *     .bar = 1;
 * }
 * This create element in table that also has distinct name that can be used directly.
 */
#define LINK_TABLE_ELEMENT(table_name, elem)                                   \
    const LINK_TABLE_ELEMENT_TYPE(table_name) elem LINK_TABLE_ELEMENT_SECTION( \
        table_name, elem)
/*
 * Macro to add existing element to link table via pointer.
 * To have table with pointers to struct like this:
 * struct base_struct {
 *    int a, b;
 * }
 * LINK_TABLE(struct base_struct *, pointer_array)
 *
 * Now create element that will go to pointer table
 * struct base_struct naked_element;
 * struct complex {
 *    const char *name;
 *    struct base_struct base;
 * } complex_obj;
 *
 * LINK_TABLE_ELEMENT_REF(pointer_array, any_name, naked_element)
 * LINK_TABLE_ELEMENT_REF(pointer_array, complex, complex_obj.base)
 */
#define LINK_TABLE_ELEMENT_REF(table_name, elem, var)                         \
    LINK_TABLE_ELEMENT_TYPE(table_name)                                       \
    const elem##_ptr LINK_TABLE_ELEMENT_SECTION(table_name, elem) = &var;

#define LINK_TABLE(element_type, table_name)                                  \
    typedef element_type table_name##_element_t;                              \
    extern element_type const table_name[];                                   \
    extern element_type const LINK_TABLE_START(table_name)[];                 \
    extern element_type const LINK_TABLE_END(table_name)[];                   \
    static inline size_t table_name##_size(void)                              \
    {                                                                         \
        return LINK_TABLE_END(table_name) - LINK_TABLE_START(table_name);     \
    }

/**
 * Simple inline loop for link table elements
 *
 * @param e - pointer variable name that can be used inside loop to access element
 * @param table - link table to iterate over
 */
#define LINK_TABLE_FOREACH(e, table)                                          \
    for (const LINK_TABLE_ELEMENT_TYPE(table) *e = table;                     \
         e != LINK_TABLE_END(table); ++e)

/**
 * Call fun for all element in link table.
 */
#define LINK_TABLE_FOREACH_CALL(table, fun)                                   \
    LINK_TABLE_FOREACH(e, table) {                                            \
        fun(e);                                                               \
    }

#endif
